Skip to content

Make Payload case-insensitive (Fixes #108)#113

Open
russ wants to merge 1 commit into
cable-cr:masterfrom
russ:fix-108-case-insensitive-payload
Open

Make Payload case-insensitive (Fixes #108)#113
russ wants to merge 1 commit into
cable-cr:masterfrom
russ:fix-108-case-insensitive-payload

Conversation

@russ

@russ russ commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Problem

Fixes #108.

Crystal's JSON parsing is case-sensitive, so a payload like {"Command": "Subscribe"} failed two separate ways:

  1. Field namePayload.from_json raised Missing JSON attribute: command because the key was Command, not command.
  2. Command value — even once parsed, Connection#receive dispatched on payload.command == "subscribe", so a Subscribe value never matched.

Fix

  • Payload.from_json normalizes the top-level keys (command / identifier / data) to lowercase before deserializing. Only the top-level keys are touched — the nested values (channel names and the identifier/data blobs) are left exactly as sent, since those stay case-sensitive (e.g. the channel name still has to match the registered channel class). Invalid payloads still raise JSON::SerializableError / JSON::ParseException as before, so the Cable::Handler error path is unchanged.
  • Connection#receive compares the command case-insensitively (payload.command.downcase).

Tests

  • A Payload unit spec parsing mixed-case top-level keys ("Command", "Identifier", "DATA") and confirming command, channel, data, and action all resolve.
  • An end-to-end #receive spec sending {"Command": "Subscribe", "Identifier": ...} and asserting it confirms the subscription.

Both specs fail on master with the exact Missing JSON attribute: command from the issue, and pass with this change. Full suite is green locally on Crystal 1.10.0 and current stable.

Note

The nightly CI job is continue-on-error: true and currently fails repo-wide because ameba won't compile against Crystal nightly (undefined method 'next_string_array_token') — it fails during shards install, before any project code, so it's unrelated to this change.

🤖 Generated with Claude Code

Crystal's JSON parsing is case-sensitive, so a payload like
`{"Command": "Subscribe"}` failed two ways:

1. `Payload.from_json` raised `Missing JSON attribute: command` because the
   key was `Command` rather than `command`.
2. Even once parsed, `Connection#receive` dispatched on `payload.command ==
   "subscribe"`, so a `Subscribe` value never matched.

Fix both:

- `Payload.from_json` normalizes the top-level keys (command/identifier/data)
  to lowercase before deserializing. Nested values — channel names and the
  identifier/data blobs — are left exactly as sent, since those stay
  case-sensitive.
- `Connection#receive` compares the command case-insensitively.

Adds a Payload unit spec for mixed-case top-level keys and an end-to-end
`#receive` spec exercising a `{"Command": "Subscribe", ...}` payload.

Fixes cable-cr#108

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMW1EsdCMExBhm3tg7TQhs
@russ russ marked this pull request as ready for review June 23, 2026 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow Payload to be case-insensitive

1 participant